﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace senController.classes
{
    class TrafficPlan
    {
        private List<TrafficLight> trafficLights;
        private Controller _controller;
        private int inQueue;
        private bool _priority;
        private int state;
        private int _busState;
        private int _maxWaitTime;
        private string _name;

        /// <summary>
        /// Initialise Trafficplan
        /// </summary>
        /// <param name="name">Name of plan for identifaction purposes</param>
        /// <param name="priority">Check if plan belongs in priority queue</param>
        /// <param name="maxWaitTime">maximal time in queue</param>
        public TrafficPlan(Controller controller, string name, bool priority = false, int maxWaitTime = 0)
        {
            this.trafficLights = new List<TrafficLight>();
            //set default settings
            this.inQueue = 0;
            this._busState = 0;
            this.state = 0;
            //set given parameters
            this._controller = controller;
            this._name = name;
            this._priority = priority;
            this._maxWaitTime = maxWaitTime;
        }

        /// <summary>
        /// Get all trafficlights included in plan
        /// </summary>
        /// <returns>List with trafficlights</returns>
        public List<TrafficLight> GetTrafficLights()
        {
            return this.trafficLights;
        }

        /// <summary>
        /// Add trafficlight to this plan
        /// </summary>
        /// <param name="trafficLight">tafficlight</param>
        public void AddTrafficLight(TrafficLight trafficLight)
        {
            trafficLights.Add(trafficLight);
            //Set trafficplan in trafficLight 
            trafficLight.SetTrafficplan(this);
        }

        /// <summary>
        /// Return the timestamp on which the plan was set into queue
        /// </summary>
        /// <returns>timestamp</returns>
        public int GetTimeInQueue()
        {
            return this.inQueue;
        }

        /// <summary>
        /// Set timestamp when plan enters queue
        /// </summary>
        /// <param name="timestamp">current timestamp</param>
        public void SetTimeInQueue(int timestamp)
        {
            this.inQueue = timestamp;
        }

        /// <summary>
        /// Check if plan needs to be moved to priority queue
        /// </summary>
        /// <param name="currentTime">current timestamp</param>
        /// <returns>true/false</returns>
        public bool MoveToPriority(int currentTime)
        {
            if (_maxWaitTime > 0 && this.inQueue + _maxWaitTime < currentTime)
                return true;                
            return false;           
        }

        /// <summary>
        /// Check if there is still traffic in the current lanes.
        /// When the trafficplan includes buslanes, don't put them back.
        /// </summary>
        /// <returns>true/false</returns>
        public bool CheckCarsInLanes()
        {
            List<TrafficLight> activeTrafficInLane = new List<TrafficLight>();

            bool cars = false;
            bool busses = false;
            foreach (TrafficLight light in trafficLights)
            {
                //Pedestrians are not registered
                if (light.GetName().Contains("1") || light.GetName().Contains("2"))
                {
                    Console.WriteLine("Pedestrians");
                    return false;
                }
                    
                //Bus?
                if (light.GetName().Contains("6"))
                {
                    if (!busses) //First bus light? return to initial state
                    {
                        busses = true;
                        cars = false;
                    }
                    if (light.GetQueueLength(this._busState) > 0) //When bus in queue, set true
                    {
                        cars = true;
                    }
                }
                //Other traffic?
                else if (light.GetQueueLength() > 0 && !light.GetName().Contains("8") && !light.GetName().Contains("1") && !light.GetName().Contains("2"))
                {
                    activeTrafficInLane.Add(light);
                    if (!busses) // When no busses, add car to queue
                       cars = true;
                }                        
            }

            //plan contained buslanes, but no busses:
            //Check if other lanes contained cars and put them in array
            if (busses && !cars)
                foreach (TrafficLight light in activeTrafficInLane)
                    _controller.ProcessSensorInformation(light.GetName(), 100);
            return cars; 
        }

        public void ClearInQueue()
        {
            this.inQueue = 0;
        }

        public int getState()
        {
            return this.state;
        }
        public void setState(int state)
        {
            this.state = state;
        }
        public void SetBusState(int state)
        {
            this._busState = state;
        }
        public int GetBusState()
        {
            return this._busState;
        }


        /// <summary>
        /// Get Direction for bus given buslight state and currentlocation
        /// </summary>
        /// <param name="currentLocation">Current location of bus (NESW)</param>
        /// <returns>direction as NESW</returns>
        public string GetBusDirection(string currentLocation)
        {
            //Translate location (NESW) to number
            int currentPole = getPoleNumber(currentLocation);

            //change direction to destination
            switch (this._busState)
            {
                case 4: //linksaf
                    currentPole += 1;
                    break;
                case 5: //rechtsaf
                    currentPole -= 1;
                    break;
                case 6: //rechtdoor
                    currentPole += 2;
                    break;
            }
            //correct pointer when out of range
            if (currentPole < 0)
                currentPole += 4;
            if (currentPole > 3)
                currentPole -= 4;
            //return correct pole
            string[] poles = { "N", "E", "S", "W" };

            return poles[currentPole];
        }

        /// <summary>
        /// Helper method. Return a number per pole
        /// </summary>
        /// <param name="currentLocation">location as NESW</param>
        /// <returns>NUMBER 0-3</returns>
        private int getPoleNumber(string currentLocation)
        {
            switch (currentLocation)
            {
                case "N":
                    return 0;
                case "E":
                    return 1;
                case "S":
                    return 2;
                case "W":
                    return 3;
                default:
                    return 0;
            }
        }


        /// <summary>
        /// Calculate greentime for this plan, taking into account length of queue
        /// </summary>
        /// <param name="timePerCar">Time for one car to pass</param>
        /// <param name="minGreenTime">min green time from settings</param>
        /// <param name="maxGreenTime">max green time from settings</param>
        /// <returns></returns>
        public int GetGreenTime(int timePerCar, int minGreenTime, int maxGreenTime)
        {
            int numberOfCars = 0, greenTime = 0;
            //Get largest amount of cars
            foreach (TrafficLight light in trafficLights)
                if (light.GetQueueLength() > numberOfCars)
                    numberOfCars = light.GetQueueLength();
            //Calculate greentime
            greenTime = numberOfCars * timePerCar;
            //Check if min or max greentime is exceeded
            if (greenTime < minGreenTime)
                greenTime = minGreenTime;
            else if (greenTime > maxGreenTime)
                greenTime = maxGreenTime;
            return greenTime;
        }

        /// <summary>
        /// Check if this plan belongs in priority queue
        /// </summary>
        /// <returns>bool</returns>
        public bool IsPriority()
        {
            return this._priority;
        }


        public string GetName()
        {
            return this._name;
        }
    }
}
